//  ****************************************************************************
//                                lcd.c
// 
//      Nokia 6610 LCD Display Driver  (Epson S1D15G00 Controller)
//
//      Controller used for LCD Display is a Epson S1D15G00 driver                                                                       
//      Note: For Olimex SAM7-EX256 boards with the G8 decal or the Sparkfun Color LCD 128x128 Nokia Knock-Off
//
//
//      We will use a 132 x 132 pixel matrix - each pixel has 12 bits of color information. 
//
//                                           _____
//                                           |   |
//                        ___________________|___|___
//                        |  ======================  |
//              ^   (2,0) |--------------------------|(0,129)                                                                  
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |     Rows|    Nokia 6610 Display    |      Note: In general, you can't view column 130 or column 131
//              |         |                          |                                         
//              X         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |                          |      
//              |         |--------------------------|
//                    (131,0)      Columns       (131,129)
//                              
//                        ------------Y-------------->      
//                      
//                                                                       
//              132 x 132 pixel matrix has two methods to specify the color info
//                           
//              1.  12 bits per pixel                                                                        
//                  requires command and 1.5 data bytes to specify a single pixel 
//                  (3 data bytes can specify 2 pixels)          
//
//              2. 8 bits per pixel
//                  requires one command and one data byte to specify the single pixel                                                                       
//                  note: pixel data byte converted by RGB table to 12-bit format above         
//
//              THIS IMPLEMENTATION USES THE 12 BITS PER PIXEL METHOD!
//                                           -------------------------
//
//
//                                                                       
//                            
//  HARDWARE INTERFACE
//  ------------------
//                                                                       
//  The Nokia 6610 display uses a SPI serial interface (9 bits)                           
//
//      PA2  = LCD Reset (set to low to reset)
//      PA12 = LCD chip select (set to low to select the LCD chip)                                                              
//      PA16 = SPI0_MISO Master In - Slave Out (not used in Olimex SAM7-EX256 LCD interface)
//      PA17 = SPI0_MOSI Master Out - Slave In pin (Serial Data to LCD slave)
//      PA18 = SPI0_SPCK  Serial Clock (to LCD slave)
//
//      SPI baud rate set to MCK/2 = 48054841/3 = 16018280 baud 
//      (period = 62 nsec, OK since 50 nsec period is min for S1D15G00)                                                               
//                                                                       
//      The important thing to note is that you CANNOT read from the LCD!                      
//
//
//  Author:   James P Lynch        August 30, 2007
//  Modified: Krzysztof Sierszecki February 24, 2008
//  ****************************************************************************


//  ****************************************************************************
//  Include Files 
//  ****************************************************************************
#include <board.h>
#include "lcd.h"


//  ****************************************************************************
//  Forward references 
//  ****************************************************************************
const unsigned char FONT6x8[97][8];
const unsigned char FONT8x8[97][8];
const unsigned char FONT8x16[97][16];


//  ****************************************************************************
//  Pointers to AT91SAM7X256 peripheral data structures 
//  ****************************************************************************
volatile    AT91PS_PIO    pPIOA   = AT91C_BASE_PIOA;
volatile    AT91PS_PIO    pPIOB   = AT91C_BASE_PIOB;
volatile    AT91PS_SPI    pSPI    = AT91C_BASE_SPI0;
volatile    AT91PS_PMC    pPMC    = AT91C_BASE_PMC;
volatile    AT91PS_PDC    pPDC    = AT91C_BASE_PDC_SPI0;



//  ****************************************************************************
//                          InitSpi( )
// 
//  Sets up SPI channel 0 for communications to Nokia 6610 LCD Display                                                          
//     
//  I/O ports used:     PA2  = LCD Reset (set to low to reset)
//                      PA12 = LCD chip select (set to low to select the LCD chip)                                                              
//                      PA16 = SPI0_MISO Master In - Slave Out (not used in LCD interface)
//                      PA17 = SPI0_MOSI Master Out - Slave In pin (Serial Data to LCD slave)
//                      PA18 = SPI0_SPCK  Serial Clock (to LCD slave)                                                                 
//                      PB20 = backlight control (normally PWM control, 1 = full on)                                                                                                                  
//
//  Author:  Olimex, James P Lynch     August 30, 2007
//  ****************************************************************************
void InitSpi(void) {

  // Pin PB20 used for LCD_BL  (backlight)
  pPIOB->PIO_OER    = BIT20;        // Configure PB20 as output
  pPIOB->PIO_SODR   = BIT20;        // Set PB20 to HIGH (backlight under PWM control - this will turn it full on)

  // Pin PA2 used for LCD_RESET
  pPIOA->PIO_OER    = BIT2;         // Configure PA2 as output
  pPIOA->PIO_SODR   = BIT2;         // Set PA2 to HIGH   (assert LCD Reset low then high to reset the controller)

  // Pin PA2 used for CS_LCD (chip select)
  pPIOA->PIO_OER    = BIT12;        // Configure PA12 as output
  pPIOA->PIO_SODR   = BIT12;        // Set PA12 to HIGH  (assert CS_LCD low to enable transmission)
  
  // Disable the following pins from PIO control (will be used instead by the SPI0 peripheral)
  //    BIT12 = PA12 -> SPI0_NPCS0 chip select
  //    BIT16 = PA16 -> SPI0_MISO Master In - Slave Out (not used in LCD interface) 
  //    BIT17 = PA17 -> SPI0_MOSI Master Out - Slave In pin (Serial Data to LCD slave)
  //    BIT18 = PA18 -> SPI0_SPCK  Serial Clock (to LCD slave)
  pPIOA->PIO_PDR = BIT12 | BIT16 | BIT17 | BIT18;   // Peripheral A Disable Register (Disable PIO control of these 4 bits)
  pPIOA->PIO_ASR = BIT12 | BIT16 | BIT17 | BIT18;   // Peripheral A Select Register (all 4 bits are in PIOA)
  pPIOA->PIO_BSR = 0;                               // Peripheral B Select Register (none of the bits are in PIOB)

  //enable the SPI0 Peripheral clock
  pPMC->PMC_PCER = 1 << AT91C_ID_SPI0;

  // SPI Control Register  SPI_CR
  pSPI->SPI_CR = AT91C_SPI_SWRST | AT91C_SPI_SPIEN;     //Software reset, SPI Enable  (0x81)
  pSPI->SPI_CR = AT91C_SPI_SPIEN;                       //SPI Enable (0x01)

  // SPI Mode Register  SPI_MR = 0xE0011
  pSPI->SPI_MR =                 
    (AT91C_SPI_DLYBCS & (0 << 24)) |        // Delay between chip selects (take default: 6 MCK periods) 
    (AT91C_SPI_PCS & (0xE << 16))  |        // Peripheral Chip Select (selects SPI_NPCS0 or PA12)                  
    (AT91C_SPI_LLB & (0 << 7))     |        // Local Loopback Enabled (disabled)
    (AT91C_SPI_MODFDIS & (1 << 4)) |        // Mode Fault Detection (disabled)
    (AT91C_SPI_PCSDEC & (0 << 2))  |        // Chip Select Decode (chip selects connected directly to peripheral)
    (AT91C_SPI_PS & (0 << 1))      |        // Peripheral Select (fixed)
    (AT91C_SPI_MSTR & (1 << 0));            // Master/Slave Mode  (Master)
  
   // SPI Chip Select Register SPI_CSR[0] = 0x01010311 
   pSPI->SPI_CSR[0] =             
    (AT91C_SPI_DLYBCT & (0x01 << 24))     | // Delay between Consecutive Transfers (32 MCK periods)
    (AT91C_SPI_DLYBS & (0x01 << 16))      | // Delay Before SPCK (1 MCK period)
    (AT91C_SPI_SCBR & (0x10 << 8))        | // Serial Clock Baud Rate (baudrate = MCK/8 = 48054841/8 = 6006855 baud
    (AT91C_SPI_BITS & (AT91C_SPI_BITS_9)) | // Bits per Transfer (9 bits)
    (AT91C_SPI_CSAAT & (0x0 << 3))        | // Chip Select Active After Transfer (is active after xfer)/
    (AT91C_SPI_NCPHA & (0x0 << 1))        | // Clock Phase (data captured on falling edge)
    (AT91C_SPI_CPOL & (0x01 << 0));         // Clock Polarity (inactive state is logic one)                      
}


//  ****************************************************************************
//                          WriteSpiCommand.c
// 
//     Writes 9-bit command to LCD display via SPI interface
//     
//     Inputs:  data  -  Leadis LDS176 controller/driver command
//                                                                    
//                                                                                   
//     Note:  clears bit 8 to indicate command transfer                                                           
//
//  Author:  Olimex, James P Lynch     August 30, 2007
//  ****************************************************************************
void WriteSpiCommand(volatile unsigned int command){
    // wait for the previous transfer to complete
    while((pSPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0);

    // clear bit 8 - indicates a "command" 
    command = (command & ~0x0100);

    // send the command
    pSPI->SPI_TDR = command;
}


//  ****************************************************************************
//                          WriteSpiData.c
// 
//     Writes 9-bit command to LCD display via SPI interface
//     
//     Inputs:  data  -  Leadis LDS176 controller/driver command
//                                                                    
//                                                                                   
//     Note:  Sets bit 8 to indicate data transfer                                                           
//
//  Author:  Olimex, James P Lynch     August 30, 2007
//  ****************************************************************************

void WriteSpiData(volatile unsigned int data){
    // wait for the transfer to complete
    while((pSPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0);

    // set bit 8, indicates "data"
    data = (data | 0x0100);

    // send the data
    pSPI->SPI_TDR = data;
}


//  ****************************************************************************
//                          Backlight.c
// 
//     Turns the backlight on and off
//     
//     Inputs:  state  -  1 = backlight on
//                        2 = backlight off
//                                                                    
//
//  Author:  Olimex, James P Lynch     August 30, 2007
//  ****************************************************************************
void Backlight(unsigned char state) {

  if(state == 1)
    pPIOB->PIO_SODR   = BIT20;    // Set PB20 to HIGH
  else
    pPIOB->PIO_CODR   = BIT20;    // Set PB20 to LOW
}


//  ****************************************************************************
//                          InitLcd.c
// 
//     Initializes the Epson S1D15G00 LCD Controller
//     
//     Inputs:  none
//
//      Author:  James P Lynch     August 30, 2007
//  ****************************************************************************
void InitLcd(void) {
    
    // Hardware reset
    LCD_RESET_LOW;
    Delay(10000);
    LCD_RESET_HIGH;
    Delay(10000);

    // Display control
    WriteSpiCommand(DISCTL);
    WriteSpiData(0x00); // P1: 0x00 = 2 divisions, switching period=8 (default)
    WriteSpiData(0x20); // P2: 0x20 = nlines/4 - 1 = 132/4 - 1 = 32)
    WriteSpiData(0x00); // P3: 0x00 = no inversely highlighted lines

    // COM scan
    WriteSpiCommand(COMSCN);
    WriteSpiData(1);    // P1: 0x01 = Scan 1->80, 160<-81

    // Internal oscilator ON
    WriteSpiCommand(OSCON);

    // Sleep out
    WriteSpiCommand(SLPOUT);

    // Power control
    WriteSpiCommand(PWRCTR);
    WriteSpiData(0x0f);   // reference voltage regulator on, circuit voltage follower on, BOOST ON

    // Inverse display
    WriteSpiCommand(DISINV);

    // Data control
    WriteSpiCommand(DATCTL);
    WriteSpiData(0x00); // P1: 0x01 = page address inverted, column address normal, address scan in column direction
    WriteSpiData(0x00); // P2: 0x00 = RGB sequence (default value)
    WriteSpiData(0x02); // P3: 0x02 = Grayscale -> 16 (selects 12-bit color, type A)

    // Voltage control (contrast setting)
    WriteSpiCommand(VOLCTR);
    WriteSpiData(34);   // P1 = 32  volume value (experiment with this value to get the best contrast)
    WriteSpiData(3);    // P2 = 3   resistance ratio (only value that works)

    // allow power supply to stabilize
    Delay(100000);

    // turn on the display
    WriteSpiCommand(DISON); 
}


//  ****************************************************************************
//                          LCDWrite130x130bmp.c
// 
//     Writes the entire screen from a bmp file 
//     Uses Olimex BmpToArray.exe utility
//
//     Inputs:  picture in bmp.h
//
//      Author:   Olimex, James P Lynch     August 30, 2007
//      Modified: Krzysztof Sierszecki      February 24,2008
//  ****************************************************************************
void LCDWrite130x130bmp(unsigned char *bmp) {
    
    long            j;          // loop counter
    
    // Data control (need to set "normal" page address for Olimex photograph)
    WriteSpiCommand(DATCTL);
    WriteSpiData(0x00); // P1: 0x00 = page address normal, column address normal, address scan in column direction
    WriteSpiData(0x03); // P2: 0x03 = RGB sequence (not default)
    WriteSpiData(0x02); // P3: 0x02 = Grayscale -> 16

    // Display OFF
    WriteSpiCommand(DISOFF);

    // Column address set  (command 0x2A)
    WriteSpiCommand(CASET);
    WriteSpiData(0);
    WriteSpiData(131);

    // Page address set  (command 0x2B)
    WriteSpiCommand(PASET);
    WriteSpiData(0);
    WriteSpiData(131);
  
    // WRITE MEMORY
    WriteSpiCommand(RAMWR);

    for(j = 0; j < 25740; j++) {
        WriteSpiData(bmp[j]);
    }
    
    // Data control (return to "inverted" page address)
    WriteSpiCommand(DATCTL);
    WriteSpiData(0x00); // P1: 0x01 = page address inverted, column address normal, address scan in column direction
    WriteSpiData(0x00); // P2: 0x00 = RGB sequence (default value)
    WriteSpiData(0x02); // P3: 0x02 = Grayscale -> 16

    // Display On
    WriteSpiCommand(DISON); 
}


//  ****************************************************************************
//                          LCDClearScreen.c
// 
//     Clears the LCD screen to single color (BLACK)
//     
//     Inputs:  none
//
//      Author:  James P Lynch     August 30, 2007
//  ****************************************************************************
void LCDClearScreen(void) {
    
    long    i;          // loop counter

    // Row address set  (command 0x2B)
    WriteSpiCommand(PASET);
    WriteSpiData(0);
    WriteSpiData(131);

    // Column address set  (command 0x2A)
    WriteSpiCommand(CASET);
    WriteSpiData(0);
    WriteSpiData(131);

    // set the display memory to BLACK
    WriteSpiCommand(RAMWR);
    for(i = 0; i < ((131 * 131) / 2); i++) {
            WriteSpiData((BLACK >> 4) & 0xFF);
            WriteSpiData(((BLACK & 0xF) << 4) | ((BLACK >> 8) & 0xF));
            WriteSpiData(BLACK & 0xFF);
    }
}


//  ****************************************************************************
//                          LCDSetPixel.c
// 
//  Lights a single pixel in the specified color at the specified x and y addresses
//     
//  Inputs:   x     =   row address (0 .. 131)
//            y     =   column address  (0 .. 131)
//            color =   12-bit color value  rrrrggggbbbb
//                      rrrr = 1111 full red
//                                :
//                             0000 red is off
//
//                      gggg = 1111 full green
//                                :
//                             0000 green is off
//
//                      bbbb = 1111 full blue
//                                :
//                             0000 blue is off
//
//     Returns:   nothing 
//
//      Note: see lcd.h for some sample color settings 
//
//      Author:  James P Lynch     August 30, 2007
//  ****************************************************************************
void LCDSetPixel(int  x, int  y, int  color) {

    // Row address set  (command 0x2B)
    WriteSpiCommand(PASET);
    WriteSpiData(x);
    WriteSpiData(x);

    // Column address set  (command 0x2A)
    WriteSpiCommand(CASET);
    WriteSpiData(y);
    WriteSpiData(y);
    
    // Now illuminate the pixel (2nd pixel will be ignored)
    WriteSpiCommand(RAMWR);
    WriteSpiData((color >> 4) & 0xFF);
    WriteSpiData(((color & 0xF) << 4) | ((color >> 8) & 0xF));
    WriteSpiData(color & 0xFF);
}


//  ****************************************************************************
//                          LCDSetLine.c
// 
//  Draws a line in the specified color from (x0,y0) to (x1,y1) 
//     
//  Inputs:   x     =   row address (0 .. 131)
//            y     =   column address  (0 .. 131)
//            color =   12-bit color value  rrrrggggbbbb
//                      rrrr = 1111 full red
//                                :
//                             0000 red is off
//
//                      gggg = 1111 full green
//                                :
//                             0000 green is off
//
//                      bbbb = 1111 full blue
//                                :
//                             0000 blue is off
//
//  Returns:   nothing 
//
//  Note: good write-up on this algorithm in Wikipedia (search for Bresenham's line algorithm)
//        see lcd.h for some sample color settings  
//
//  Authors:    Dr. Leonard McMillan, Associate Professor UNC
//              Jack Bresenham IBM, Winthrop University (Father of this algorithm, 1962)
//
//              Note: taken verbatim from Professor McMillan's presentation: 
//                    http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html
//
//  ****************************************************************************
void LCDSetLine(int x0, int y0, int x1, int y1, int color) {

        int dy = y1 - y0;
        int dx = x1 - x0;
        int stepx, stepy;

        if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
        if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
        dy <<= 1;                           // dy is now 2*dy
        dx <<= 1;                           // dx is now 2*dx

        LCDSetPixel(x0, y0, color);
        if (dx > dy) {
            int fraction = dy - (dx >> 1);  // same as 2*dy - dx
            while (x0 != x1) {
                if (fraction >= 0) {
                    y0 += stepy;
                    fraction -= dx;         // same as fraction -= 2*dx
                }
                x0 += stepx;
                fraction += dy;             // same as fraction -= 2*dy
                LCDSetPixel(x0, y0, color);
            }
        } else {
            int fraction = dx - (dy >> 1);
            while (y0 != y1) {
                if (fraction >= 0) {
                    x0 += stepx;
                    fraction -= dy;
                }
                y0 += stepy;
                fraction += dx;
                LCDSetPixel(x0, y0, color);
            }
        }
}


//  ****************************************************************************
//                          LCDSetRect.c
// 
//  Draws a rectangle in the specified color from (x1,y1) to (x2,y2)
//  Rectangle can be filled with a color if desired 
//     
//  Inputs:   x     =   row address (0 .. 131)
//            y     =   column address  (0 .. 131)
//            fill  =   0=no fill, 1-fill entire rectangle 
//            color =   12-bit color value for lines    rrrrggggbbbb
//                      rrrr = 1111 full red
//                                :
//                             0000 red is off
//
//                      gggg = 1111 full green
//                                :
//                             0000 green is off
//
//                      bbbb = 1111 full blue
//                                :
//                             0000 blue is off
//
//     Returns:   nothing 
//
//      Notes:
//
//      The best way to fill a rectangle is to take advantage of the "wrap-around" featute
//      built into the Epson S1D15G00 controller. By defining a drawing box, the memory can
//      be simply filled by successive memory writes until all pixels have been illuminated.
//
//          1.  Given the coordinates of two opposing corners (x0, y0) (x1, y1)
//              calculate the minimums and maximums of the coordinates
//
//                  xmin = (x0 <= x1) ? x0 : x1;
//                  xmax = (x0 > x1) ? x0 : x1;
//                  ymin = (y0 <= y1) ? y0 : y1;
//                  ymax = (y0 > y1) ? y0 : y1;
//
//          2. Now set up the drawing box to be the desired rectangle
//
//                  WriteSpiCommand(PASET);     // set the row boundaries
//                  WriteSpiData(xmin);
//                  WriteSpiData(xmax);
//                  WriteSpiCommand(CASET);     // set the column boundaries
//                  WriteSpiData(ymin);
//                  WriteSpiData(ymax);
//
//          3. Calculate the number of pixels to be written divided by 2
//
//                  NumPixels = ((((xmax - xmin + 1) * (ymax - ymin + 1)) / 2) + 1)
//
//                      You may notice that I added one pixel to the formula. 
//                      This covers the case where the number of pixels is odd and we 
//                      would lose one pixel due to rounding error. In the case of
//                      odd pixels, the number of pixels is exact. 
//                      in the case of even pixels, we have one more pixel than
//                      needed, but it cannot be displayed because it is outside
//                      the drawing box.
//
//                      We divide by 2 because two pixels are represented by three bytes.
//                      So we work through the rectangle two pixels at a time.
//
//          4.  Now a simple memory write loop will fill the rectangle
//
//              for (i = 0; i < ((((xmax - xmin + 1) * (ymax - ymin + 1)) / 2) + 1); i++) {
//                  WriteSpiData((color >> 4) & 0xFF);
//                  WriteSpiData(((color & 0xF) << 4) | ((color >> 8) & 0xF));
//                  WriteSpiData(color & 0xFF);
//              }
//
//
//      In the case of an unfilled rectangle, drawing four lines with the Bresenham line
//      drawing algorithm is reasonably efficient.
//
//
//      Author:  James P Lynch      August 30, 2007
//  ****************************************************************************

void LCDSetRect(int x0, int y0, int x1, int y1, unsigned char fill, int color) {
    int     xmin, xmax, ymin, ymax;
    int     i;
    
    // check if the rectangle is to be filled
    if (fill == FILL) {
        
        // best way to create a filled rectangle is to define a drawing box
        // and loop two pixels at a time
        
        // calculate the min and max for x and y directions
        xmin = (x0 <= x1) ? x0 : x1;
        xmax = (x0 > x1) ? x0 : x1;
        ymin = (y0 <= y1) ? y0 : y1;
        ymax = (y0 > y1) ? y0 : y1;
        
        // specify the controller drawing box according to those limits
        // Row address set  (command 0x2B)
        WriteSpiCommand(PASET);
        WriteSpiData(xmin);
        WriteSpiData(xmax);
        
        // Column address set  (command 0x2A)
        WriteSpiCommand(CASET);
        WriteSpiData(ymin);
        WriteSpiData(ymax);
        
        // WRITE MEMORY
        WriteSpiCommand(RAMWR);
        
        // loop on total number of pixels / 2
        for (i = 0; i < ((((xmax - xmin + 1) * (ymax - ymin + 1)) / 2) + 130); i++) {
            
            // use the color value to output three data bytes covering two pixels
            WriteSpiData((color >> 4) & 0xFF);
            WriteSpiData(((color & 0xF) << 4) | ((color >> 8) & 0xF));
            WriteSpiData(color & 0xFF);
        }

    } else {
    
        // best way to draw un unfilled rectangle is to draw four lines
        LCDSetLine(x0, y0, x1, y0, color);
        LCDSetLine(x0, y1, x1, y1, color);
        LCDSetLine(x0, y0, x0, y1, color);
        LCDSetLine(x1, y0, x1, y1, color);
    }
}



//  ****************************************************************************
//                          LCDSetCircle.c
// 
//  Draws a line in the specified color at center (x0,y0) with radius 
//     
//  Inputs:   x0     =   row address (0 .. 131)
//            y0     =   column address  (0 .. 131)
//            radius =  radius in pixels
//            color  =  12-bit color value  rrrrggggbbbb
//
//  Returns:   nothing  
//
//  Author:     Jack Bresenham IBM, Winthrop University (Father of this algorithm, 1962)
//
//              Note: taken verbatim Wikipedia article on Bresenham's line algorithm  
//                    http://www.wikipedia.org
//
//  ****************************************************************************

void LCDSetCircle(int x0, int y0, int radius, int color) {
    int f = 1 - radius;
    int ddF_x = 0;
    int ddF_y = -2 * radius;
    int x = 0;
    int y = radius;
 
    LCDSetPixel(x0, y0 + radius, color);
    LCDSetPixel(x0, y0 - radius, color);
    LCDSetPixel(x0 + radius, y0, color);
    LCDSetPixel(x0 - radius, y0, color);
 
    while(x < y) {
        if(f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x + 1;    
        LCDSetPixel(x0 + x, y0 + y, color);
        LCDSetPixel(x0 - x, y0 + y, color);
        LCDSetPixel(x0 + x, y0 - y, color);
        LCDSetPixel(x0 - x, y0 - y, color);
        LCDSetPixel(x0 + y, y0 + x, color);
        LCDSetPixel(x0 - y, y0 + x, color);
        LCDSetPixel(x0 + y, y0 - x, color);
        LCDSetPixel(x0 - y, y0 - x, color);
    }
}


//  ****************************************************************************
//                          LCDPutChar.c
// 
//     Draws an ASCII character at the specified (x,y) address and color
//     
//     Inputs:    c       =   character to be displayed       
//                x       =   row address (0 .. 131)
//                y       =   column address  (0 .. 131)
//                size    =   font pitch (SMALL, MEDIUM, LARGE)
//                fcolor  =   12-bit foreground color value     rrrrggggbbbb
//                bcolor  =   12-bit background color value     rrrrggggbbbb
//
//
//     Returns:   nothing  
//
//  Notes:  Here's an example to display "E" at address (20,20)
//
//          LCDPutChar('E', 20, 20, MEDIUM, WHITE, BLACK);
//
//                   (27,20)        (27,27)
//                      |             |
//                      |             |
//                   ^  V             V
//                   :  _ # # # # # # #   0x7F
//                   :  _ _ # # _ _ _ #   0x31
//                   :  _ _ # # _ # _ _   0x34
//                   x  _ _ # # # # _ _   0x3C
//                   :  _ _ # # _ # _ _   0x34
//                   :  _ _ # # _ _ _ #   0x31
//                   :  _ # # # # # # #   0x7F
//                   :  _ _ _ _ _ _ _ _   0x00
//
//                      ------y----------->
//                      ^             ^
//                      |             |
//                      |             |
//                   (20,20)       (20,27)
//
//
//      The most efficient way to display a character is to make use of the "wrap-around" feature
//      of the Epson S1D16G00 LCD controller chip.  
//
//      Assume that we position the character at (20, 20)  that's a  (row, col) specification.
//      With the row and column address set commands, you can specify an 8x8 box for the SMALL and MEDIUM
//      characters or a 16x8 box for the LARGE characters.
//
//          WriteSpiCommand(PASET);     // set the row drawing limits
//          WriteSpiData(20);           //
//          WriteSpiData(27);           // limit rows to (20, 27)
// 
//          WriteSpiCommand(CASET);     // set the column drawing limits
//          WriteSpiData(20);           // 
//          WriteSpiData(27);           // limit columns to (20,27)
//
//      When the algorithm completes col 27, the column address wraps back to 20
//      At the same time, the row address increases by one (this is done by the controller)
//
//      We walk through each row, two pixels at a time. The purpose is to create three
//      data bytes representing these two pixels in the following format
//
//          Data for pixel 0:  RRRRGGGGBBBB
//          Data for Pixel 1:  RRRRGGGGBBBB
//
//          WriteSpiCommand(RAMWR);             // start a memory write (96 data bytes to follow)
//
//          WriteSpiData(RRRRGGGG);             // first pixel, red and green data
//          WriteSpiData(BBBBRRRR);             // first pixel, blue data; second pixel, red data
//          WriteSpiData(GGGGBBBB);             // second pixel, green and blue data
//              :
//      and so on until all pixels displayed!
//              :
//          WriteSpiCommand(NOP);               // this will terminate the RAMWR command
//
//
//      Author:  James P Lynch    August 30, 2007
//  **************************************************************************** 
void LCDPutChar(char c, int  x, int  y, int size, int fColor, int bColor) {
    
    extern const unsigned char FONT6x8[97][8];
    extern const unsigned char FONT8x8[97][8];
    extern const unsigned char FONT8x16[97][16];
        
    int                 i,j;
    unsigned int        nCols;
    unsigned int        nRows;
    unsigned int        nBytes;
    unsigned char       PixelRow;
    unsigned char       Mask;
    unsigned int        Word0;
    unsigned int        Word1;
    unsigned char       *pFont;
    unsigned char       *pChar;
    unsigned char       *FontTable[] = {(unsigned char *)FONT6x8, (unsigned char *)FONT8x8, (unsigned char *)FONT8x16}; 
    
    // get pointer to the beginning of the selected font table
    pFont = (unsigned char *)FontTable[size];   
    
    // get the nColumns, nRows and nBytes
    nCols = *pFont;
    nRows = *(pFont + 1);
    nBytes = *(pFont + 2);

    // get pointer to the last byte of the desired character
    pChar = pFont + (nBytes * (c - 0x1F));
    
    // Row address set  (command 0x2B)
    WriteSpiCommand(PASET);
    WriteSpiData(x);
    WriteSpiData(x + nRows - 1);
  
    // Column address set  (command 0x2A)
    WriteSpiCommand(CASET);
    WriteSpiData(y);
    WriteSpiData(y + nCols - 1);
    
    // WRITE MEMORY
    WriteSpiCommand(RAMWR);
    
    // loop on each row, working backwards from the bottom to the top
    for (i = nRows - 1; i >= 0; i--) {
        
        // copy pixel row from font table and then decrement row
        PixelRow = *pChar++;
    
        // loop on each pixel in the row (left to right)
        // Note: we do two pixels each loop
        Mask = 0x80;
        for (j = 0; j < nCols; j += 2) {
        
            // if pixel bit set, use foreground color; else use the background color
            // now get the pixel color for two successive pixels
            if ((PixelRow & Mask) == 0)
                Word0 = bColor;
            else
                Word0 = fColor;
            Mask = Mask >> 1;
            if ((PixelRow & Mask) == 0)
                Word1 = bColor;
            else
                Word1 = fColor;
            Mask = Mask >> 1;
            
            // use this information to output three data bytes
            WriteSpiData((Word0 >> 4) & 0xFF);
            WriteSpiData(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF));
            WriteSpiData(Word1 & 0xFF);
        }   
    }
    // terminate the Write Memory command
    WriteSpiCommand(NOP);   
}


//  ****************************************************************************
//                          LCDPutStr.c
// 
//     Draws a null-terminates character string at the specified (x,y) address, size and color
//     
//     Inputs:    pString =   pointer to character string to be displayed         
//                x       =   row address (0 .. 131)
//                y       =   column address  (0 .. 131)
//                Size    =   font pitch (SMALL, MEDIUM, LARGE)
//                fColor  =   12-bit foreground color value             rrrrggggbbbb
//                bColor  =   12-bit background color value             rrrrggggbbbb
//
//
//     Returns:   nothing  
//
//  Notes:  Here's an example to display "Hello World!" at address (20,20)
//
//          LCDPutChar("Hello World!", 20, 20, LARGE, WHITE, BLACK);
//
//
//      Author:  James P Lynch    August 30, 2007
//  ****************************************************************************
void LCDPutStr(char *pString, int  x, int  y, int Size, int fColor, int bColor) {
                        
    // loop until null-terminator is seen
    while (*pString != 0x00) {

        // draw the character
        LCDPutChar(*pString++, x, y, Size, fColor, bColor); 

        // advance the y position
        if (Size == SMALL)
            y = y + 6;
        else if (Size == MEDIUM)
            y = y + 8;
        else
            y = y + 8;
        
        // bail out if y exceeds 131
        if (y > 131) break;
    }
}


//  ****************************************************************************
//                          Delay.c
// 
//      Simple for loop delay
//     
//      Inputs: a  -  loop count
//
//      Author:  James P Lynch  August 30, 2007
//  ****************************************************************************
void Delay (unsigned long a) { 
    while (--a!=0) {
       asm volatile ("mov r0, r0"); // nop
    }
}


//  ****************************************************************************
// 
//      Font tables for Nokia 6610 LCD Display Driver  (S1D15G00 Controller)
//
//      FONT6x8     -  SMALL font (mostly 5x7)
//      FONT8x8     -  MEDIUM font (8x8 characters, a bit thicker)
//      FONT8x16    -  LARGE font  (8x16 characters, thicker)
//                                                                       
//      Note: ASCII characters 0x00 through 0x1F are not included in these fonts.                            
//
//  Author:  Jim Parise, James P Lynch  August 30, 2007
//  ****************************************************************************
const unsigned char FONT6x8[97][8] =  {     
{ 0x06,0x08,0x08,0x00,0x00,0x00,0x00,0x00 },    //  columns, rows, num_bytes_per_char
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  space   0x20
{ 0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00 },    //  !
{ 0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00 },    //  "
{ 0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00 },    //  #   
{ 0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00 },    //  $
{ 0xC0,0xC8,0x10,0x20,0x40,0x98,0x18,0x00 },    //  %
{ 0x40,0xA0,0xA0,0x40,0xA8,0x90,0x68,0x00 },    //  &
{ 0x30,0x30,0x20,0x40,0x00,0x00,0x00,0x00 },    //  '
{ 0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00 },    //  (
{ 0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00 },    //  )
{ 0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00 },    //  *
{ 0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00 },    //  +
{ 0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40 },    //  ,
{ 0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00 },    //  -
{ 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00 },    //  .
{ 0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00 },    //  / (forward slash)
{ 0x70,0x88,0x88,0xA8,0x88,0x88,0x70,0x00 },    //  0       0x30
{ 0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00 },    //  1
{ 0x70,0x88,0x08,0x70,0x80,0x80,0xF8,0x00 },    //  2
{ 0xF8,0x08,0x10,0x30,0x08,0x88,0x70,0x00 },    //  3
{ 0x10,0x30,0x50,0x90,0xF8,0x10,0x10,0x00 },    //  4
{ 0xF8,0x80,0xF0,0x08,0x08,0x88,0x70,0x00 },    //  5
{ 0x38,0x40,0x80,0xF0,0x88,0x88,0x70,0x00 },    //  6
{ 0xF8,0x08,0x08,0x10,0x20,0x40,0x80,0x00 },    //  7
{ 0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00 },    //  8
{ 0x70,0x88,0x88,0x78,0x08,0x10,0xE0,0x00 },    //  9
{ 0x00,0x00,0x20,0x00,0x20,0x00,0x00,0x00 },    //  :
{ 0x00,0x00,0x20,0x00,0x20,0x20,0x40,0x00 },    //  ;
{ 0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00 },    //  <
{ 0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00 },    //  =
{ 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00 },    //  >
{ 0x70,0x88,0x08,0x30,0x20,0x00,0x20,0x00 },    //  ?
{ 0x70,0x88,0xA8,0xB8,0xB0,0x80,0x78,0x00 },    //  @       0x40
{ 0x20,0x50,0x88,0x88,0xF8,0x88,0x88,0x00 },    //  A
{ 0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00 },    //  B
{ 0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00 },    //  C
{ 0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00 },    //  D
{ 0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00 },    //  E
{ 0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00 },    //  F
{ 0x78,0x88,0x80,0x80,0x98,0x88,0x78,0x00 },    //  G
{ 0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00 },    //  H
{ 0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00 },    //  I
{ 0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00 },    //  J
{ 0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x00 },    //  K
{ 0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00 },    //  L
{ 0x88,0xD8,0xA8,0xA8,0xA8,0x88,0x88,0x00 },    //  M
{ 0x88,0x88,0xC8,0xA8,0x98,0x88,0x88,0x00 },    //  N
{ 0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00 },    //  O
{ 0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00 },    //  P       0x50
{ 0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00 },    //  Q
{ 0xF0,0x88,0x88,0xF0,0xA0,0x90,0x88,0x00 },    //  R
{ 0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00 },    //  S
{ 0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x00 },    //  T
{ 0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00 },    //  U
{ 0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00 },    //  V
{ 0x88,0x88,0x88,0xA8,0xA8,0xA8,0x50,0x00 },    //  W
{ 0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00 },    //  X
{ 0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00 },    //  Y
{ 0xF8,0x08,0x10,0x70,0x40,0x80,0xF8,0x00 },    //  Z
{ 0x78,0x40,0x40,0x40,0x40,0x40,0x78,0x00 },    //  [
{ 0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00 },    //  \  (back slash)
{ 0x78,0x08,0x08,0x08,0x08,0x08,0x78,0x00 },    //  ]   
{ 0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00 },    //  ^
{ 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00 },    //  _
{ 0x60,0x60,0x20,0x10,0x00,0x00,0x00,0x00 },    //  `       0x60
{ 0x00,0x00,0x60,0x10,0x70,0x90,0x78,0x00 },    //  a
{ 0x80,0x80,0xB0,0xC8,0x88,0xC8,0xB0,0x00 },    //  b
{ 0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00 },    //  c
{ 0x08,0x08,0x68,0x98,0x88,0x98,0x68,0x00 },    //  d
{ 0x00,0x00,0x70,0x88,0xF8,0x80,0x70,0x00 },    //  e
{ 0x10,0x28,0x20,0x70,0x20,0x20,0x20,0x00 },    //  f
{ 0x00,0x00,0x70,0x98,0x98,0x68,0x08,0x70 },    //  g
{ 0x80,0x80,0xB0,0xC8,0x88,0x88,0x88,0x00 },    //  h
{ 0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00 },    //  i
{ 0x10,0x00,0x10,0x10,0x10,0x90,0x60,0x00 },    //  j
{ 0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x00 },    //  k
{ 0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00 },    //  l
{ 0x00,0x00,0xD0,0xA8,0xA8,0xA8,0xA8,0x00 },    //  m
{ 0x00,0x00,0xB0,0xC8,0x88,0x88,0x88,0x00 },    //  n
{ 0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00 },    //  o
{ 0x00,0x00,0xB0,0xC8,0xC8,0xB0,0x80,0x80 },    //  p       0x70
{ 0x00,0x00,0x68,0x98,0x98,0x68,0x08,0x08 },    //  q
{ 0x00,0x00,0xB0,0xC8,0x80,0x80,0x80,0x00 },    //  r
{ 0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00 },    //  s
{ 0x20,0x20,0xF8,0x20,0x20,0x28,0x10,0x00 },    //  t
{ 0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00 },    //  u
{ 0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00 },    //  v
{ 0x00,0x00,0x88,0x88,0xA8,0xA8,0x50,0x00 },    //  w
{ 0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00 },    //  x
{ 0x00,0x00,0x88,0x88,0x78,0x08,0x88,0x70 },    //  y
{ 0x00,0x00,0xF8,0x10,0x20,0x40,0xF8,0x00 },    //  z
{ 0x10,0x20,0x20,0x40,0x20,0x20,0x10,0x00 },    //  {
{ 0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00 },    //  |
{ 0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00 },    //  }
{ 0x40,0xA8,0x10,0x00,0x00,0x00,0x00,0x00 },    //  ~
{ 0x70,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00 }  // DEL
};

const unsigned char FONT8x8[97][8] = {  
{ 0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 },    //  columns, rows, num_bytes_per_char
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  space   0x20
{ 0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00 },    //  !
{ 0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00 },    //  "
{ 0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00 },    //  #   
{ 0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00 },    //  $
{ 0x00,0x63,0x66,0x0C,0x18,0x33,0x63,0x00 },    //  %
{ 0x1C,0x36,0x1C,0x3B,0x6E,0x66,0x3B,0x00 },    //  &
{ 0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00 },    //  '
{ 0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00 },    //  (
{ 0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00 },    //  )
{ 0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00 },    //  *
{ 0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00 },    //  +
{ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30 },    //  ,
{ 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00 },    //  -
{ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00 },    //  .
{ 0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00 },    //  / (forward slash)
{ 0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00 },    //  0       0x30
{ 0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00 },    //  1
{ 0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00 },    //  2
{ 0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00 },    //  3
{ 0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00 },    //  4
{ 0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00 },    //  5
{ 0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00 },    //  6
{ 0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00 },    //  7
{ 0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00 },    //  8
{ 0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00 },    //  9
{ 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00 },    //  :
{ 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30 },    //  ;
{ 0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00 },    //  <
{ 0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00 },    //  =
{ 0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00 },    //  >
{ 0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00 },    //  ?
{ 0x3E,0x63,0x6F,0x69,0x6F,0x60,0x3E,0x00 },    //  @       0x40
{ 0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x00 },    //  A
{ 0x7E,0x33,0x33,0x3E,0x33,0x33,0x7E,0x00 },    //  B
{ 0x1E,0x33,0x60,0x60,0x60,0x33,0x1E,0x00 },    //  C
{ 0x7C,0x36,0x33,0x33,0x33,0x36,0x7C,0x00 },    //  D
{ 0x7F,0x31,0x34,0x3C,0x34,0x31,0x7F,0x00 },    //  E
{ 0x7F,0x31,0x34,0x3C,0x34,0x30,0x78,0x00 },    //  F
{ 0x1E,0x33,0x60,0x60,0x67,0x33,0x1F,0x00 },    //  G
{ 0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00 },    //  H
{ 0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00 },    //  I
{ 0x0F,0x06,0x06,0x06,0x66,0x66,0x3C,0x00 },    //  J
{ 0x73,0x33,0x36,0x3C,0x36,0x33,0x73,0x00 },    //  K
{ 0x78,0x30,0x30,0x30,0x31,0x33,0x7F,0x00 },    //  L
{ 0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00 },    //  M
{ 0x63,0x73,0x7B,0x6F,0x67,0x63,0x63,0x00 },    //  N
{ 0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00 },    //  O
{ 0x7E,0x33,0x33,0x3E,0x30,0x30,0x78,0x00 },    //  P       0x50
{ 0x3C,0x66,0x66,0x66,0x6E,0x3C,0x0E,0x00 },    //  Q
{ 0x7E,0x33,0x33,0x3E,0x36,0x33,0x73,0x00 },    //  R
{ 0x3C,0x66,0x30,0x18,0x0C,0x66,0x3C,0x00 },    //  S
{ 0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00 },    //  T
{ 0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x00 },    //  U
{ 0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00 },    //  V
{ 0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00 },    //  W
{ 0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00 },    //  X
{ 0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00 },    //  Y
{ 0x7F,0x63,0x46,0x0C,0x19,0x33,0x7F,0x00 },    //  Z
{ 0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00 },    //  [
{ 0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00 },    //  \  (back slash)
{ 0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00 },    //  ]   
{ 0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00 },    //  ^
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF },    //  _
{ 0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00 },    //  `       0x60
{ 0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00 },    //  a
{ 0x70,0x30,0x3E,0x33,0x33,0x33,0x6E,0x00 },    //  b
{ 0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00 },    //  c
{ 0x0E,0x06,0x3E,0x66,0x66,0x66,0x3B,0x00 },    //  d
{ 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00 },    //  e
{ 0x1C,0x36,0x30,0x78,0x30,0x30,0x78,0x00 },    //  f
{ 0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x7C },    //  g
{ 0x70,0x30,0x36,0x3B,0x33,0x33,0x73,0x00 },    //  h
{ 0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00 },    //  i
{ 0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C },    //  j
{ 0x70,0x30,0x33,0x36,0x3C,0x36,0x73,0x00 },    //  k
{ 0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00 },    //  l
{ 0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00 },    //  m
{ 0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00 },    //  n
{ 0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00 },    //  o
{ 0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78 },    //  p       0x70
{ 0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F },    //  q
{ 0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00 },    //  r
{ 0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00 },    //  s
{ 0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00 },    //  t
{ 0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00 },    //  u
{ 0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00 },    //  v
{ 0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00 },    //  w
{ 0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00 },    //  x
{ 0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x7C },    //  y
{ 0x00,0x00,0x7E,0x4C,0x18,0x32,0x7E,0x00 },    //  z
{ 0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00 },    //  {
{ 0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x0C,0x00 },    //  |
{ 0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00 },    //  }
{ 0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00 },    //  ~
{ 0x1C,0x36,0x36,0x1C,0x00,0x00,0x00,0x00 }   //    DEL
};


const unsigned char FONT8x16[97][16] = {
{ 0x08,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  columns, rows, num_bytes_per_char
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  space   0x20
{ 0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00 },    //  !
{ 0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  "
{ 0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00 },    //  #   
{ 0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00 },    //  $
{ 0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00 },    //  %
{ 0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00 },    //  &
{ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  '
{ 0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00 },    //  (
{ 0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00 },    //  )
{ 0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00 },    //  *
{ 0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00 },    //  +
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00 },    //  ,
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  -
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00 },    //  .
{ 0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00 },    //  / (forward slash)
{ 0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  0       0x30
{ 0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00 },    //  1
{ 0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00 },    //  2
{ 0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  3
{ 0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00 },    //  4
{ 0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00 },    //  5
{ 0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  6
{ 0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00 },    //  7
{ 0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  8
{ 0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00 },    //  9
{ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00 },    //  :
{ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00 },    //  ;
{ 0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00 },    //  <
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00 },    //  =
{ 0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00 },    //  >
{ 0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00 },    //  ?
{ 0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00 },    //  @       0x40
{ 0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00 },    //  A
{ 0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00 },    //  B
{ 0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00 },    //  C
{ 0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00 },    //  D
{ 0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00 },    //  E
{ 0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00 },    //  F
{ 0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00 },    //  G
{ 0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00 },    //  H
{ 0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00 },    //  I
{ 0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00 },    //  J
{ 0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00 },    //  K
{ 0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00 },    //  L
{ 0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00 },    //  M
{ 0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00 },    //  N
{ 0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00 },    //  O
{ 0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00 },    //  P       0x50
{ 0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00 },    //  Q
{ 0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00 },    //  R
{ 0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  S
{ 0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00 },    //  T
{ 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  U
{ 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00 },    //  V
{ 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00 },    //  W
{ 0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00 },    //  X
{ 0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00 },    //  Y
{ 0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00 },    //  Z
{ 0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00 },    //  [
{ 0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00 },    //  \  (back slash)
{ 0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00 },    //  ]   
{ 0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  ^
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00 },    //  _
{ 0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  `       0x60
{ 0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00 },    //  a
{ 0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00 },    //  b
{ 0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  c
{ 0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00 },    //  d
{ 0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  e
{ 0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00 },    //  f
{ 0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00 },    //  g
{ 0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00 },    //  h
{ 0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00 },    //  i
{ 0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00 },    //  j
{ 0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00 },    //  k
{ 0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00 },    //  l
{ 0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00 },    //  m
{ 0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00 },    //  n
{ 0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  o
{ 0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00 },    //  p       0x70
{ 0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00 },    //  q
{ 0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00 },    //  r
{ 0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00 },    //  s
{ 0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00 },    //  t
{ 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00 },    //  u
{ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00 },    //  v
{ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00 },    //  w
{ 0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00 },    //  x
{ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00 },    //  y
{ 0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00 },    //  z
{ 0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00 },    //  {
{ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00 },    //  |
{ 0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00 },    //  }
{ 0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },    //  ~
{ 0x00,0x70,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } //  DEL
};
